/// ------------------------------------------------------------------------------------------------------------------------------------
///
/// MIT License
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///
/// Copyright (c) 2023 ycz. All rights reserved.
///
/// Created by ycz on  2022/1/2.
///
/// @file  y_comm_m2m.c
///
/// @brief
///     y_comm_m2m 是一种多对多通信服务 可以方便的实现嵌入式设备间的通信
///
/// ------------------------------------------------------------------------------------------------------------------------------------



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 头文件
/// ------------------------------------------------------------------------------------------------------------------------------------

#include "y_comm_m2m.h"

#include <string.h>
#include <stdlib.h>
#include "y_sa.h"



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 宏定义
/// ------------------------------------------------------------------------------------------------------------------------------------

#ifdef _Y_LL_H
#    define Y_MALLOC y_ll_malloc
#    define Y_FREE   y_ll_free
#else
#    define Y_MALLOC malloc
#    define Y_FREE   free
#endif



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 枚举
/// ------------------------------------------------------------------------------------------------------------------------------------

/// 连接模块命令枚举
typedef enum {
    LINK_ACK,        ///< ack 回应
    LINK_TIME_SYNC,  ///< 时间同步
    LINK_TIME_ROOT,  ///< 同步 root mac 到子设备
} LINK_e;



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 全局变量
/// ------------------------------------------------------------------------------------------------------------------------------------

static COMM_M2M_CONFIG_st g_default_config = {
        .wan_id         = 0,     ///< 网络 id
        .retry_count    = 10,    ///< 消息发送重试次数
        .clear_time_s   = 15,    ///< 缓存消息清空时间 S
        .rssi_threshold = -120,  ///< 无线通信时信号阈值
        .is_root        = true,  ///< 是否是根节点
        .root_mac       = {0},   ///< root mac 地址
};  ///< 默认参数



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 私有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   通信服务 添加消息到发送数组
/// @param   [in] handle                   句柄
/// @param   [in] msg                      消息指针
/// @param   [in] send_count               发送次数
static void _y_comm_m2m_add_msg_to_send_arr(COMM_M2M_st *handle, PROTOCOL_M2M_V1_st *msg, uint8_t send_count) {

    // 遍历列表寻找保存位置
    for (int i = 0; i < Y_COMM_SEND_ARR_SIZE; ++i) {
        if (handle->send_arr[i].msg == NULL) {
            handle->send_arr[i].msg   = y_protocol_dump(msg);
            handle->send_arr[i].count = send_count == 0 ? 1 : send_count;
            return;  // 保存成功后直接返回
        }
    }
    // 没有找到保存位置 则丢弃最早的一条消息
    y_protocol_delete(handle->send_arr[0].msg);
    for (int i = 0; i < Y_COMM_SEND_ARR_SIZE - 1; ++i) {
        handle->send_arr[i].msg   = handle->send_arr[i + 1].msg;
        handle->send_arr[i].count = handle->send_arr[i + 1].count;
    }
    handle->send_arr[Y_COMM_SEND_ARR_SIZE - 1].msg   = y_protocol_dump(msg);
    handle->send_arr[Y_COMM_SEND_ARR_SIZE - 1].count = send_count == 0 ? 1 : send_count;
    YLOGI("loss msg");  // todo
}

/// @brief   处理发送消息数组
/// @param   [in] handle                   句柄
/// @retval  true                          发送了一次消息
/// @retval  false                         没有发送消息
static bool _y_comm_m2m_handle_send_arr(COMM_M2M_st *handle) {

    // 检查 发送列表 是否有消息需要发送
    if (handle->send_arr[0].msg) {

        // 检查 此条发送消息有没有收到 ack
        for (int i = 0; i < Y_COMM_SEND_ARR_SIZE; ++i) {
            if (handle->ack_arr[i].num == handle->send_arr[0].msg->head.num && y_utils_cmp_mac6(handle->ack_arr[i].mac, handle->send_arr[0].msg->recv_mac)) {
                // 删除此条 ack 记录
                handle->ack_arr[i].time = 0;
                handle->ack_arr[i].num  = 0;
                memset(handle->ack_arr[i].mac, 0, sizeof(handle->ack_arr[i].mac));
                // 删除此条发送消息
                y_protocol_delete(handle->send_arr[0].msg);
                for (int j = 0; j < Y_COMM_SEND_ARR_SIZE - 1; ++j) {
                    handle->send_arr[j].msg   = handle->send_arr[j + 1].msg;
                    handle->send_arr[j].count = handle->send_arr[j + 1].count;
                }
                // 最后一个位置置为空
                handle->send_arr[Y_COMM_SEND_ARR_SIZE - 1].msg   = NULL;
                handle->send_arr[Y_COMM_SEND_ARR_SIZE - 1].count = 0;
                return false;  // 收到回应 不在重发
            }
        }

        // 判断是不是时间同步消息
        if (handle->send_arr[0].msg->mod_id == MOD_LINK && handle->send_arr[0].msg->cmd_id == LINK_TIME_SYNC && handle->send_arr[0].msg->cmd_data) {
            uint64_t timestamp_now = y_time_get_timestamp();  // 获取当前时间
            if (timestamp_now < 1262275200000) {
                return false;  // 获取时间失败 则不处理消息
            }
            memcpy(handle->send_arr[0].msg->cmd_data, &timestamp_now, sizeof(uint64_t));  // 更新为本机的当前时间 同步给下一个设备
        }

        // 发送数据
        uint8_t *send_data = NULL;
        uint16_t send_size = 0;
        if (y_protocol_pack(handle->send_arr[0].msg, &send_data, &send_size)) {
            if (handle->send_cb) {
                handle->send_cb(handle->send_arr[0].msg->recv_mac, send_data, send_size);  // 回调发送数据
            }
            Y_FREE(send_data);            // 释放空间
            handle->send_arr[0].count--;  // 重试次数减一
            if (handle->send_arr[0].count) {
                return true;  // 还需要重发
            }
            // YLOGI(MAC_FORMAT " send num %d", MAC_TO_STR(handle->send_arr[0].msg->send_mac), handle->send_arr[0].msg->head.num);
        }

        // 删除此条发送消息  重试结束 或者 打包失败
        y_protocol_delete(handle->send_arr[0].msg);
        for (int j = 0; j < Y_COMM_SEND_ARR_SIZE - 1; ++j) {
            handle->send_arr[j].msg   = handle->send_arr[j + 1].msg;
            handle->send_arr[j].count = handle->send_arr[j + 1].count;
        }
        // 最后一个位置置为空
        handle->send_arr[Y_COMM_SEND_ARR_SIZE - 1].msg   = NULL;
        handle->send_arr[Y_COMM_SEND_ARR_SIZE - 1].count = 0;
        return true;
    }

    return false;
}

/// @brief   处理接收消息数组
/// @param   [in] handle                   句柄
static void _y_comm_m2m_handle_recv_arr(COMM_M2M_st *handle) {

    // 判断是否需要处理消息
    if (handle->lock) {
        return;
    }

    // 循环数组处理消息
    for (int i = 0; i < Y_COMM_RECV_ARR_SIZE; ++i) {
        if (handle->recv_arr[i].msg) {

            // 消息去重
            if (handle->recv_arr[i].msg->head.num) {
                // 遍历是否收到过相同消息
                bool is_filter = false;  // 是否过滤标志
                for (int j = 0; j < Y_COMM_FILTER_ARR_SIZE; ++j) {
                    if (handle->filter_arr[j].num == handle->recv_arr[i].msg->head.num && y_utils_cmp_mac6(handle->filter_arr[j].mac, handle->recv_arr[i].msg->send_mac)) {
                        handle->filter_arr[j].time = 0;     // 更新时间戳 重新开始过滤计时
                        is_filter                  = true;  // 过滤此消息
                        break;                              // 跳出循环
                    }
                }
                if (is_filter) {
                    y_protocol_delete(handle->recv_arr[i].msg);  // 过滤成功后 删除此消息
                    continue;                                    // 处理下一条消息
                }
                // 第一次收到此消息 记录消息信息 方便下次消息过滤
                bool is_save = false;  // 是否保存
                for (int h = 0; h < Y_COMM_FILTER_ARR_SIZE; ++h) {
                    if (handle->filter_arr[h].num == 0) {
                        handle->filter_arr[h].time = 0;
                        handle->filter_arr[h].num  = handle->recv_arr[i].msg->head.num;
                        memcpy(handle->filter_arr[h].mac, handle->recv_arr[i].msg->send_mac, sizeof(handle->recv_arr[i].msg->send_mac));
                        is_save = true;  // 保存成功
                        break;           // 跳出循环
                    }
                }
                if (is_save == false) {
                    for (int k = 0; k < Y_COMM_FILTER_ARR_SIZE - 1; ++k) {
                        memcpy(&handle->filter_arr[k], &handle->filter_arr[k + 1], sizeof(handle->filter_arr[k]));
                    }
                    handle->filter_arr[Y_COMM_FILTER_ARR_SIZE - 1].time = 0;
                    handle->filter_arr[Y_COMM_FILTER_ARR_SIZE - 1].num  = handle->recv_arr[i].msg->head.num;
                    memcpy(handle->filter_arr[Y_COMM_FILTER_ARR_SIZE - 1].mac, handle->recv_arr[i].msg->send_mac, sizeof(handle->recv_arr[i].msg->send_mac));
                }
            }

            // 判断消息是否重复 二次过滤 去掉间隔重复消息
            static PROTOCOL_M2M_V1_st last_msg           = {0};
            static PROTOCOL_M2M_V1_st last_last_msg      = {0};
            static PROTOCOL_M2M_V1_st last_last_last_msg = {0};
            if (memcmp(&last_msg, handle->recv_arr[i].msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *)) == 0
                || memcmp(&last_last_msg, handle->recv_arr[i].msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *)) == 0
                || memcmp(&last_last_last_msg, handle->recv_arr[i].msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *)) == 0) {
                // 去掉历史重复和间隔重复的消息
                y_protocol_delete(handle->recv_arr[i].msg);  // 过滤成功后 删除此消息
                continue;                                    // 处理下一条消息
            } else {
                // 更新历史消息
                memcpy(&last_last_last_msg, &last_last_msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *));
                memcpy(&last_last_msg, &last_msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *));
                memcpy(&last_msg, handle->recv_arr[i].msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *));
            }

            // 消息转发  root 不参与消息转发  不在此转发连接模块消息
            if (handle->config.is_root == false) {
                if (handle->recv_arr[i].msg->mod_id != MOD_LINK && handle->recv_arr[i].msg->dev_type == y_sys_get_dev_type()) {
                    _y_comm_m2m_add_msg_to_send_arr(handle, handle->recv_arr[i].msg, handle->config.retry_count - 2);  // 帮忙无脑转发
                }
            }

            // 消息筛选   不是发给自己的消息 不是广播消息 不是发给 root 的消息 不处理
            if (!(y_utils_cmp_mac6(handle->recv_arr[i].msg->recv_mac, y_sys_get_mac()) || y_utils_cmp_mac6(handle->recv_arr[i].msg->recv_mac, g_mac6_broadcast)
                  || y_utils_cmp_mac6(handle->recv_arr[i].msg->recv_mac, g_mac6_root))) {
                y_protocol_delete(handle->recv_arr[i].msg);  // 筛选成功后 删除消息
                continue;                                    // 处理下一条消息
            }

            // 发送回应消息  (广播消息不回应) todo: 根据场景考虑对发送给root的消息进行回应
            // if (y_utils_cmp_mac6(handle->recv_arr[i].msg->recv_mac, y_sys_get_mac())) {
            // PROTOCOL_M2M_V1_st ack_msg = y_protocol_create_m2m_msg(handle->recv_arr[i].msg->head.num,
            //                                                        y_sys_get_mac(),
            //                                                        handle->recv_arr[i].msg->send_mac,
            //                                                        y_sys_get_dev_type(),
            //                                                        y_sys_get_dev_num(),
            //                                                        handle->config.wan_id ? handle->config.wan_id : y_comm_m2m_get_group_wan_id(handle->config.root_mac),
            //                                                        MOD_LINK,
            //                                                        LINK_ACK,
            //                                                        NULL,
            //                                                        0);  // 创建 ack 消息
            // _y_comm_m2m_add_msg_to_send_arr(handle, &ack_msg, 1);       // 发送回应消息
            // }

            // 业务消息处理
            if (handle->recv_arr[i].msg->mod_id == MOD_LINK) {

                // 连接模块消息处理
                switch (handle->recv_arr[i].msg->cmd_id) {

                    case LINK_ACK: {  // 回应消息
                        // 保存 ack 回应信息
                        bool is_save = false;  // 是否保存
                        for (int k = 0; k < Y_COMM_ACK_ARR_SIZE; ++k) {
                            if (handle->ack_arr[k].num == 0) {
                                handle->ack_arr[k].time = 0;
                                handle->ack_arr[k].num  = handle->recv_arr[i].msg->head.num;
                                memcpy(handle->ack_arr[i].mac, handle->recv_arr[i].msg->send_mac, sizeof(handle->recv_arr[i].msg->send_mac));
                                is_save = true;  // 保存成功
                                break;           // 跳出循环
                            }
                        }
                        if (is_save == false) {
                            for (int j = 0; j < Y_COMM_ACK_ARR_SIZE - 1; ++j) {
                                memcpy(&handle->ack_arr[j], &handle->ack_arr[j + 1], sizeof(handle->ack_arr[j]));
                            }
                            handle->ack_arr[Y_COMM_ACK_ARR_SIZE - 1].time = 0;
                            handle->ack_arr[Y_COMM_ACK_ARR_SIZE - 1].num  = handle->recv_arr[i].msg->head.num;
                            memcpy(handle->ack_arr[Y_COMM_ACK_ARR_SIZE - 1].mac, handle->recv_arr[i].msg->send_mac, sizeof(handle->recv_arr[i].msg->send_mac));
                        }
                    } break;

                    case LINK_TIME_SYNC:  // 同步时间 需要马上处理
                        // 网关端 不同步设备或则其他网关的时间
                        if (handle->config.is_root == false && handle->recv_arr[i].msg->cmd_data && handle->recv_arr[i].msg->cmd_size == sizeof(uint64_t)) {
                            // 判断是否需要 转发时间同步消息
                            if (y_utils_cmp_mac6(handle->config.root_mac, g_mac6_empty) || y_utils_cmp_mac6(handle->config.root_mac, handle->recv_arr[i].msg->send_mac)) {
                                uint64_t timestamp_now = y_time_get_timestamp();
                                uint64_t timestamp_ms  = *(uint64_t *) &handle->recv_arr[i].msg->cmd_data[0];
                                int64_t  time_diff     = (int64_t) (timestamp_now - timestamp_ms);
                                // 判断当前时间是否正确
                                if (timestamp_now > 1262275200000) {
                                    // 时间差值相差超过 10 ms 则更新一次时间设置 设置时间不能太频繁
                                    if (abs((int) time_diff) >= 10) {
                                        if (abs((int) time_diff) > 500 || time_diff < 0) {
                                            y_time_set_timestamp(timestamp_ms);  // 设置时间 用于第一次设置 时间从未进行过同步
                                        } else {
                                            y_time_set_timestamp(timestamp_ms + time_diff);  // 设置时间
                                        }
                                    }
                                    YLOGD("time sync ok, time_diff  %8lld  rssi %d", time_diff, handle->recv_arr[i].rssi);
                                }
                                // 保存到发送数组中
                                _y_comm_m2m_add_msg_to_send_arr(handle, handle->recv_arr[i].msg, handle->config.retry_count);  // 将要发送的消息 添加到发送数组中 等待发送
                            }
                        }
                        break;

                    case LINK_TIME_ROOT:  // root 同步消息
                        if (handle->recv_arr[i].msg->cmd_data) {
                            for (int h = 0; h < handle->recv_arr[h].msg->cmd_size / 6; ++h) {
                                if (y_utils_cmp_mac6(y_sys_get_mac(), &handle->recv_arr[i].msg->cmd_data[h * 6])) {
                                    memcpy(handle->config.root_mac, &handle->recv_arr[i].msg->cmd_data[h * 6], 6);  // 设置 root mac
                                    handle->config.is_root = false;                                                 // 更改为子设备
                                    y_comm_m2m_config_set(handle, &handle->config);                                 // 设置到 nvs
                                }
                            }
                        }
                        break;
                }

            } else {
                // 其他模块消息处理
                // YLOGI(MAC_FORMAT " recv num %d   %d", MAC_TO_STR(handle->recv_arr[i].msg->send_mac), handle->recv_arr[i].msg->head.num, i);
                handle->business_cb(handle->recv_arr[i].msg, handle->recv_arr[i].rssi);  // 回调处理消息
            }

            y_protocol_delete(handle->recv_arr[i].msg);  // 处理完成后 删除消息
            continue;                                    // 处理下一条消息 直到处理完所有消息
        }
    }

    // 清空数组
    for (int i = 0; i < Y_COMM_RECV_ARR_SIZE; ++i) {
        handle->recv_arr[i].rssi = 0;
        handle->recv_arr[i].msg  = NULL;
    }
    handle->lock = true;  // 更新临界区状态 处理完成
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印 y_comm_m2m 版本信息
void y_comm_m2m_print_version() {
    YLOG_VERSION("y_comm_m2m", Y_COMM_M2M_MAJOR, Y_COMM_M2M_MINOR, Y_COMM_M2M_PATCH);
}

/// @brief   通信服务 获取小组网络 id
/// @param   [in] mac                      mac 地址
uint8_t y_comm_m2m_get_group_wan_id(uint8_t *mac) {
    YLOGA_FALSE(mac);
    uint64_t mac_num = 0;      // mac 地址转化为数字
    memcpy(&mac_num, mac, 6);  // mac 转 数字
    return mac_num % 0XFF;     // mac 数字 求余 0XFF 获得一个 0-255 的 wan_id 作为当前小组的 wan_id
}

/// @brief   通信服务 数据解析
/// @param   [in] handle                   句柄
/// @param   [in] data                     数据指针
/// @param   [in] size                     数据大小
/// @param   [in] rssi                     信号强度
/// @retval  true                          成功
/// @retval  false                         失败
bool y_comm_m2m_parse(COMM_M2M_st *handle, uint8_t *data, uint16_t size, int16_t rssi) {

    // 断言
    YLOGA_FALSE(handle);
    YLOGA_FALSE(data);
    if (size < sizeof(PROTOCOL_M2M_V1_st) - sizeof(uint8_t *)) {
        return false;  // 去掉消息长度不够的消息
    }

    // ble 原始数据去重
    if (size <= 512) {
        static uint8_t last_data[512] = {0};
        if (memcmp(last_data, data, size) == 0) {
            return false;  // 原始数据重复 不在处理
        }
        memset(last_data, 0, 512);
        memcpy(last_data, data, size);
    }

    // 解析协议数据
    PROTOCOL_M2M_V1_st *msg = NULL;
    if (y_protocol_unpack(data, size, (void **) &msg)) {

        // YLOGW("RSSI %d",rssi);
        // YLOG_DATA("recv", data, size);

        // 判断消息类型是不是该服务能解析的
        if (msg->head.type != PROTOCOL_M2M_V1) {
            y_protocol_delete(msg);  // 使用结束 删除消息
            return true;
        }

        // 判断 rssi 阈值
        if (handle->config.rssi_threshold) {
            if (rssi < handle->config.rssi_threshold) {
                y_protocol_delete(msg);  // 使用结束 删除消息
                return false;            // 超过此消息设置的 rssi 范围 不进行处理
            }
        }

        // 判断 wan id 是否匹配
        if (handle->config.wan_id != msg->wan_id && y_comm_m2m_get_group_wan_id(handle->config.is_root ? y_sys_get_mac() : handle->config.root_mac) != msg->wan_id) {
            y_protocol_delete(msg);  // 网路 id 不匹配 删除消息  不是手动设置的id 也不是自动设置的组 网络id
            return true;
        }

        // 自己发送的消息不处理
        if (memcmp(msg->send_mac, y_sys_get_mac(), sizeof(msg->send_mac)) == 0) {
            y_protocol_delete(msg);  // 使用结束 删除消息
            return true;
        }

        // 消息去重
        for (int i = 0; i < Y_COMM_FILTER_ARR_SIZE; ++i) {
            if (msg->head.num && handle->filter_arr[i].num == msg->head.num && y_utils_cmp_mac6(handle->filter_arr[i].mac, msg->send_mac)) {
                handle->filter_arr[i].time = 0;  // 更新时间计数
                y_protocol_delete(msg);          // 重复消息 删除消息
                return true;                     // 过滤成功
            }
        }

        // 将消息放到缓存到临时数组
        static struct {
            int16_t             rssi;  ///< rssi
            PROTOCOL_M2M_V1_st *msg;   ///< 消息
        } tmp_recv_arr[Y_COMM_RECV_ARR_SIZE];
        bool is_delete = true;  // 是否删除本条消息
        for (int i = 0; i < Y_COMM_RECV_ARR_SIZE; ++i) {
            if (tmp_recv_arr[i].msg == NULL) {
                tmp_recv_arr[i].msg  = msg;
                tmp_recv_arr[i].rssi = rssi;
                is_delete            = false;  // 不删除本条消息
                break;
            }
        }
        // 判断是否需要删除消息
        if (is_delete) {
            YLOGI("tmp_recv full");
            y_protocol_delete(msg);  // 保存失败 则删除消息
        }

        // 判断临界区状态 将消息转移到业务模块取去处理
        if (handle->lock) {
            // 转存数组 并清空数组
            for (int i = 0; i < Y_COMM_RECV_ARR_SIZE; ++i) {
                handle->recv_arr[i].msg  = tmp_recv_arr[i].msg;
                handle->recv_arr[i].rssi = tmp_recv_arr[i].rssi;
                tmp_recv_arr[i].msg      = NULL;  // 清空临时数组
                tmp_recv_arr[i].rssi     = 0;     // 清空临时数组
            }
            handle->lock = false;  // 更新临界区状态
        }

        return true;
    }

    return false;
}

/// @brief   通信服务 发送消息
/// @param   [in] handle                   句柄
/// @param   [in] recv_mac                 接收值 mac 地址 (NULL 表示发送到root)
/// @param   [in] dev_type                 设备类型
/// @param   [in] mod_id                   模块 id
/// @param   [in] cmd_id                   命令 id
/// @param   [in] cmd_data                 命令数据指针
/// @param   [in] cmd_size                 命令数据大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_comm_m2m_send(COMM_M2M_st *handle, uint8_t *recv_mac, uint8_t dev_type, uint8_t mod_id, uint8_t cmd_id, uint8_t *cmd_data, uint16_t cmd_size) {

    // 断言
    YLOGA_FALSE(handle);
    if (cmd_size > 256) {
        YLOGW("cmd_size over 256, send fail!!!");
        return false;
    }

    // 创建要发送的消息
    PROTOCOL_M2M_V1_st msg = y_protocol_create_m2m_msg(0,
                                                       y_sys_get_mac(),
                                                       recv_mac,
                                                       dev_type,
                                                       y_sys_get_dev_num(),
                                                       handle->config.wan_id ? handle->config.wan_id : y_comm_m2m_get_group_wan_id(handle->config.root_mac),
                                                       mod_id,
                                                       cmd_id,
                                                       cmd_data,
                                                       cmd_size);
    // 修正参数
    if (recv_mac == NULL) {
        if (y_utils_cmp_mac6(handle->config.root_mac, g_mac6_empty) == false) {
            memcpy(msg.recv_mac, handle->config.root_mac, sizeof(msg.recv_mac));  // 未指定接收对象 但root不为空 发送数据
        } else {
            memcpy(msg.recv_mac, g_mac6_broadcast, sizeof(msg.recv_mac));  // 未指定接收对象 并且root不存在 发送广播数据
        }
    }

    // 将要发送的消息 添加到发送数组中 等待发送
    _y_comm_m2m_add_msg_to_send_arr(handle, &msg, handle->config.retry_count);

    return true;
}

/// @brief   通信服务 同步 root mac 到子设备  由 root 发起 子节点接收并自动设置为自己的 root
/// @param   [in] handle                   句柄
/// @param   [in] dev_type                 设备类型
/// @param   [in] mac_list                 子设备列表
/// @param   [in] size                     大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_comm_m2m_sync_root(COMM_M2M_st *handle, uint8_t dev_type, uint8_t *mac_list, uint16_t size) {
    return y_comm_m2m_send(handle, g_mac6_broadcast, dev_type, MOD_LINK, LINK_TIME_ROOT, mac_list, size);
}

/// @brief   通信服务 时间同步
/// @param   [in] handle                   句柄
/// @param   [in] dev_type                 设备类型
/// @param   [in] time_ms                  毫秒时间戳
/// @retval  true                          成功
/// @retval  false                         失败
bool y_comm_m2m_sync_time(COMM_M2M_st *handle, uint8_t dev_type, uint64_t time_ms) {
    time_ms = time_ms / 1000 * 1000;
    return y_comm_m2m_send(handle, g_mac6_broadcast, dev_type, MOD_LINK, LINK_TIME_SYNC, (uint8_t *) &time_ms, sizeof(uint64_t));
}

/// @brief   打印参数
/// @param   [in] handle                   句柄
/// @param   [in] config                   参数
void y_comm_m2m_config_print(COMM_M2M_st *handle, COMM_M2M_CONFIG_st *config) {
    YLOGA(config);
    YLOGI("COMM_M2M %d wan_id                 : %d", handle->nvs_num, config->wan_id);
    YLOGI("COMM_M2M %d retry_count            : %d", handle->nvs_num, config->retry_count);
    YLOGI("COMM_M2M %d rssi_threshold         : %d", handle->nvs_num, config->rssi_threshold);
    YLOGI("COMM_M2M %d clear_time_s           : %d", handle->nvs_num, config->clear_time_s);
    YLOGI("COMM_M2M %d group wan_id           : %d", handle->nvs_num, y_comm_m2m_get_group_wan_id(config->is_root ? y_sys_get_mac() : config->root_mac));  // 打印组 wan_id
    YLOGI("COMM_M2M %d root_mac               : " MAC_FORMAT, handle->nvs_num, MAC_TO_STR(config->root_mac));
}

/// @brief   获取通信服务参数
/// @param   [in] handle                   句柄
/// @return  通信服务参数
COMM_M2M_CONFIG_st *y_comm_m2m_config_get(COMM_M2M_st *handle) {
    // 断言
    YLOGA_NULL(handle);
    return &handle->config;
}

/// @brief   设置通信服务参数
/// @param   [in] handle                   句柄
/// @param   [in] config                   通信服务参数
/// @retval  true                          成功
/// @retval  false                         失败
bool y_comm_m2m_config_set(COMM_M2M_st *handle, COMM_M2M_CONFIG_st *config) {
    // 断言
    YLOGA_FALSE(handle);
    YLOGA_FALSE(config);
    memcpy(&handle->config, config, sizeof(handle->config));  // 参数赋值
    char nvs_name[16] = {0};
    sprintf(nvs_name, "m2m_config_%d", handle->nvs_num);
    return y_nvs_set((uint8_t *) nvs_name, (uint8_t *) &handle->config, sizeof(handle->config), true);
}

/// @brief   创建通信服务
/// @return  通信服务句柄
COMM_M2M_st *y_comm_m2m_init() {

    // 创建通信服务句柄
    COMM_M2M_st *handle = (COMM_M2M_st *) Y_MALLOC(sizeof(COMM_M2M_st));
    YLOGA_NULL(handle);
    memset(handle, 0, sizeof(COMM_M2M_st));

    // 加载算法参数
    static uint8_t nvs_num = 1;
    handle->nvs_num        = nvs_num++;
    char nvs_name[16]      = {0};
    sprintf(nvs_name, "m2m_config_%d", handle->nvs_num);
    uint16_t size = 0;
    if (y_nvs_get((uint8_t *) nvs_name, (uint8_t *) &handle->config, &size) == false || size != sizeof(handle->config)) {  // 从 nvs 读取失败
        memcpy(&handle->config, &g_default_config, sizeof(handle->config));
        y_nvs_set((uint8_t *) nvs_name, (uint8_t *) &handle->config, sizeof(handle->config), true);
    }
    y_comm_m2m_config_print(handle, &handle->config);  // 打印参数

    return handle;
}

/// @brief   通信服务任务处理
/// @param   [in] handle                   句柄
/// @param   [in] time_ms                  每次调用间隔时间
/// @retval  true                          发送了一次消息
/// @retval  false                         没有发送消息
bool y_comm_m2m_process(COMM_M2M_st *handle, uint32_t time_ms) {

    // 断言
    YLOGA_FALSE(handle);

    bool ret = _y_comm_m2m_handle_send_arr(handle);  // 处理发送消息
    _y_comm_m2m_handle_recv_arr(handle);             // 处理接收消息

    for (int i = 0; i < Y_COMM_ACK_ARR_SIZE; ++i) {
        // 检查 ack 数组 去除超时的 ack 消息
        if (handle->ack_arr[i].num) {
            handle->ack_arr[i].time += time_ms;                                     // 计时
            if (handle->ack_arr[i].time >= handle->config.clear_time_s * 1000) {    // 计时结束 删除消息
                handle->ack_arr[i].time = 0;                                        // 删除此条 ack 记录
                handle->ack_arr[i].num  = 0;                                        // 删除此条 ack 记录
                memset(handle->ack_arr[i].mac, 0, sizeof(handle->ack_arr[i].mac));  // 删除此条 ack 记录
            }
        }
    }
    for (int i = 0; i < Y_COMM_FILTER_ARR_SIZE; ++i) {
        // 检查 过滤数组 去除超时的消息
        if (handle->filter_arr[i].num) {
            handle->filter_arr[i].time += time_ms;                                        // 计时
            if (handle->filter_arr[i].time >= handle->config.clear_time_s * 1000) {       // 计时结束 删除消息
                handle->filter_arr[i].time = 0;                                           // 删除此条 过滤消息 记录
                handle->filter_arr[i].num  = 0;                                           // 删除此条 过滤消息 记录
                memset(handle->filter_arr[i].mac, 0, sizeof(handle->filter_arr[i].mac));  // 删除此条 过滤消息 记录
            }
        }
    }

    return ret;
}
